// ignore_for_file: implementation_imports

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:collection/collection.dart';
import 'package:riki_router/core/riki_argument.dart';
import 'package:riki_router/core/riki_route.dart';
import 'package:riki_router_library/src/arg/args.dart';
import 'package:riki_router_library/src/argument_info.dart';
import 'package:riki_router_library/src/file_info.dart';
import 'package:riki_router_library/src/initialization_info.dart';
import 'package:riki_router_library/src/utils/dart_type_auto_import.dart';
import 'package:source_gen/source_gen.dart';

import 'route_info_base.dart';

TypeChecker rikiArgumentTypeChecker = const TypeChecker.fromRuntime(RikiArgument);

class RouteInfo extends RouteInfoBase {
  RouteInfo({
    required RikiRoute rikiRoute,
    required String className,
    required this.classElement,
    required FileInfo fileInfo,
    required String packageName,
  }) : super(
          className: className,
          rikiRoute: rikiRoute,
          fileInfo: fileInfo,
          packageName: packageName,
        );

  final ClassElement classElement;

  List<ConstructorElement> get constructors => classElement.constructors.toList();

  List<String> get prefixes => classElement.library.prefixes.map((PrefixElement e) => e.displayName).toList();

  @override
  String? get constructorsString {
    if (constructors.isNotEmpty) {
      String temp = '';
      for (final ConstructorElement rawConstructor in constructors) {
        if (constructors.length == 1 && rawConstructor.parameters.isEmpty && rawConstructor.name.isEmpty) {
          return null;
        }

        final String args = rawConstructor.toString().replaceFirst(rawConstructor.returnType.toString(), '').trim();

        temp += '\n /// \n /// $args';
      }
      return temp;
    }

    return null;
  }

  @override
  String get constructor {
    constructors.removeWhere((ConstructorElement element) => element.name.toString() == '_');
    if (constructors.isNotEmpty) {
      if (constructors.length > 1) {
        String keyValues = '';
        for (final ConstructorElement rawConstructor in constructors) {
          keyValues += '\'${rawConstructor.name}\': ${getConstructorString(rawConstructor)}';
          keyValues += ',';
        }
        return '<String,Widget>{$keyValues}[safeArguments[constructorName]!=null? safeArguments[constructorName] as String:\'\']';
      } else {
        return '${getConstructorString(constructors.first)}';
      }
    }
    return '$className()';
  }

  String getIsOptional(String name, ParameterElement parameter, ConstructorElement rawConstructor) {
    String value = 'safeArguments[\'$name\']';

    final String type = getParameterType(parameter);

    value = 'asT<$type>($value';

    final String? defaultValueCode = DartTypeAutoImportHelper().getDefaultValueString(parameter, prefixes);

    if (defaultValueCode != null) {
      value += ',$defaultValueCode';
    }

    value += ',)';
    if (Args().enableNullSafety && !type.endsWith('?')) {
      value += '!';
    }

    if (!parameter.isPositional) {
      value = '$name:' + value;
    }
    return value;
  }

  String getConstructorString(ConstructorElement rawConstructor) {
    String constructorString = '';

    constructorString = getConstructor(rawConstructor);

    constructorString += '(';
    bool hasParameters = false;

    //final List<FormalParameter> optionals = <FormalParameter>[];

    for (final ParameterElement item in rawConstructor.parameters) {
      final String name = item.name;
      hasParameters = true;
      if (item.isOptional || item.isRequiredNamed) {
        constructorString += getIsOptional(name, item, rawConstructor);
        // if (!item.isRequired) {
        //   optionals.add(item);
        // }
      } else {
        final String type = getParameterType(item);

        constructorString += 'asT<$type>(safeArguments[\'$name\'])';
        if (Args().enableNullSafety && !type.endsWith('?')) {
          constructorString += '!';
        }
      }

      constructorString += ',';
    }

    constructorString += ')';

    if (rawConstructor.isConst && !hasParameters) {
      constructorString = 'const ' + constructorString;
    }
    return constructorString;
  }

  String getParameterType(ParameterElement parameter) {
    return DartTypeAutoImportHelper().fixDartTypeString(parameter.type);
  }

  String getConstructor(ConstructorElement rawConstructor) {
    String ctor = className;
    if (rawConstructor.name.isNotEmpty) {
      ctor += '.${rawConstructor.name}';
    }

    return classNameConflictPrefixText + ctor;
  }

  @override
  String? getArgumentsClass() {
    constructors.removeWhere((ConstructorElement element) => element.name == '_');
    if (constructors.isNotEmpty) {
      final StringBuffer sb = StringBuffer();

      for (final ConstructorElement rawConstructor in constructors) {
        final String name = rawConstructor.name;
        if (constructors.length == 1 && name.isEmpty && rawConstructor.parameters.isEmpty) {
          // only one ctor and no parameters
          // no need arguments class
          return null;
        }

        String args = DartTypeAutoImportHelper().getFormalParameters(rawConstructor.parameters, prefixes);

        String nameMap = '';
        final List<String> parameterNames = <String>[];
        for (final ParameterElement parameter in rawConstructor.parameters) {
          final String name = parameter.name;
          if (!Args().enableNullSafety) {
            args = args.replaceAll('?', '');
          }
          nameMap += ''''$name':$name,''';
          parameterNames.add('\'$name\'');
        }

        nameMap += ''''$constructorName':'$name',''';
        // if (Args().enableSupperArguments && Args().enableArgumentNames) {
        //   nameMap += ''''$argumentNames':<String>$parameterNames,''';
        // }

        sb.write(routeConstClassMethodTemplate
            .replaceAll('{0}', (name.isEmpty ? 'd' : rawConstructor.name) + args)
            .replaceAll('{1}', nameMap)
            .replaceAll('{2}', rawConstructor.parameters.isEmpty ? 'const' : ''));
      }

      if (sb.isNotEmpty) {
        argumentsClass = sb.toString();
        return argumentsClass;
      }
    }

    return null;
  }

  @override
  List<InitializationInfo>? get initializations {
    final List<InitializationInfo> initializationInfoList = []; //当前所有初始化方法
    final List<ArgumentInfo> arguments = <ArgumentInfo>[]; //当前所有参数
    classElement.fields.forEach((element) {
      String name = element.name;
      String meaning = '';

      /// 是否存在备注信息
      final DartObject? rikiArgumentAnnotation = rikiArgumentTypeChecker.firstAnnotationOf(
        element,
        throwOnUnresolved: true,
      );
      if (rikiArgumentAnnotation != null) {
        final ConstantReader reader = ConstantReader(rikiArgumentAnnotation);
        meaning = reader.peek('meaning')?.stringValue ?? '';
      }
      ArgumentInfo argumentInfo = ArgumentInfo(
        name: name,
        type: '',
        meaning: meaning,
        required: false,
      );
      arguments.add(argumentInfo);
    });
    constructors.forEach((element) {
      final List<ArgumentInfo> argumentsTemp = <ArgumentInfo>[]; //当前方法所有参数
      element.parameters.forEach((item) {
        final String name = item.name;
        final String type = item.type.toString();
        ArgumentInfo? info = arguments.firstWhereOrNull((e) => e.name == name);
        bool isRequired = false;
        String meaning = '';
        if (info != null) {
          isRequired = item.isRequiredNamed;
          meaning = info.meaning;
        }
        argumentsTemp.add(ArgumentInfo(name: name, type: type, meaning: meaning, required: isRequired));
      });
      initializationInfoList.add(InitializationInfo(name: element.name.isEmpty ? '默认' : element.name, arguments: argumentsTemp));
    });
    return initializationInfoList;
  }
}
